//
// Created by ASUS on 2023/12/1.
//

#ifndef TPOOL_THREADPOOL_HPP
#define TPOOL_THREADPOOL_HPP

#include <thread>
#include <mutex>
#include <future>
#include <functional>
#include <queue>
#include <cassert>

namespace tpool {

class ThreadPool
{
public:
    using MutexGuard = std::lock_guard<std::mutex>;
    using Uniquelock = std::unique_lock<std::mutex>;
    using Thread = std::thread;
    using ThreadID = std::thread::id;
    using Task = std::function<void()>;

    ThreadPool()
        : ThreadPool(Thread::hardware_concurrency())
    {}

    explicit ThreadPool(uint64_t maxThreads)
        : quit_(false),
          currentThreads_(0),
          idleThreads_(0),
          maxThreads_(maxThreads)
    {}

    //禁止拷贝.将拷贝构造函数和拷贝赋值运算符声明为 delete
    ThreadPool(const ThreadPool &) = delete;
    ThreadPool &operator=(const ThreadPool &) = delete;

    ~ThreadPool()
    {
        {
            MutexGuard guard(mutex_);
            quit_ = true;
        }
        cv_.notify_all();
        for (auto &elem : threads_) {
            assert(elem.second.joinable());
            elem.second.join();
        }
    }

    template<typename Func, typename... Ts>
    auto submit(Func &&func, Ts &&... params)
        -> std::future<typename std::result_of<Func(Ts...)>::type>
    {
        auto execute = std::bind(std::forward<Func>(func), std::forward<Ts>(params)...);
        using ReturnType = typename std::result_of<Func(Ts...)>::type;
        using PackagedTask = std::packaged_task<ReturnType()>;

        auto task = std::make_shared<PackagedTask>(std::move(execute));
        auto result = task->get_future();
        MutexGuard guard(mutex_);
        assert(!quit_);

        tasks_.emplace([task]()
                       {
                           (*task)();
                       });
        if (idleThreads_ > 0)
        {
            cv_.notify_one();
        } else if (currentThreads_ < maxThreads_)
        {
            Thread t(&ThreadPool::worker, this);
            assert(threads_.find(t.get_id()) == threads_.end());
            threads_[t.get_id()] = std::move(t);
            ++currentThreads_;
        }
        return result;
    }

    uint64_t threadsNum() const
    {
        MutexGuard guard(mutex_);
        return currentThreads_;
    }
private:
    void worker()
    {
        while (true)
        {
            Task task;
            {
                Uniquelock uniquelock(mutex_);
                ++idleThreads_;
                auto hasTimeout = !cv_.wait_for(uniquelock,
                                                std::chrono::seconds(WAIT_SECONDS),
                                                [this]()
                                                {
                                                    return quit_ || !tasks_.empty();
                                                });
                --idleThreads_;
                if (tasks_.empty())
                {
                    if (quit_)
                    {
                        --currentThreads_;
                        return;
                    }
                    if (hasTimeout)
                    {
                        --currentThreads_;
                        joinFinishedThreads();
                        finishedThreadIDs_.emplace(std::this_thread::get_id());
                        return;
                    }
                }
                task = std::move(tasks_.front());
                tasks_.pop();
            }
            task();
        }
    }

    void joinFinishedThreads()
    {
        while (!finishedThreadIDs_.empty())
        {
            auto id = std::move(finishedThreadIDs_.front());
            finishedThreadIDs_.pop();
            auto iter = threads_.find(id);
            assert(iter != threads_.end());
            assert(iter->second.joinable());
            iter->second.join();
            threads_.erase(iter);
        }
    }


    static constexpr uint8_t               WAIT_SECONDS = 2;

    bool                                   quit_;
    uint64_t                               currentThreads_;
    uint64_t                               idleThreads_;
    uint64_t                               maxThreads_;

    mutable std::mutex                    mutex_;
    std::condition_variable               cv_;
    std::queue<Task>                      tasks_;
    std::queue<ThreadID>                  finishedThreadIDs_;
    std::unordered_map<ThreadID, Thread>  threads_;
};
constexpr uint8_t ThreadPool::WAIT_SECONDS;
}

#endif //TPOOL_THREADPOOL_HPP
